use abci::ServerBuilder;
use dzk::{cfg::DaemonCfg, rpc::Web3ServerBuilder, App};
use ruc::*;
use std::{fs, net::SocketAddr};
use tendermint_config::{net::Address as TmAddr, TendermintConfig};

pub fn exec(cfg: DaemonCfg) -> Result<()> {
    let app = App::load_or_create(cfg).c(d!())?;

    start_web3_service(&app).c(d!())?;

    let (addr, is_unix_sock) = get_tm_proxy_laddr(&app.cfg).c(d!())?;
    let addr = if is_unix_sock {
        return Err(eg!("Unsupported protocol"));
    } else {
        addr.parse::<SocketAddr>().c(d!())?
    };

    println!("**** ABCI server listen at: {} ****", addr);
    ServerBuilder::new(256 * 1024 * 1024)
        .bind(addr, app)
        .c(d!())?
        .listen()
        .c(d!())
}

fn start_web3_service(app: &App) -> Result<()> {
    let http = if 0 == app.cfg.serv_http_port {
        None
    } else {
        Some(format!("{}:{}", &app.cfg.serv_addr, app.cfg.serv_http_port))
    };
    let ws = if 0 == app.cfg.serv_ws_port {
        None
    } else {
        Some(format!("{}:{}", &app.cfg.serv_addr, app.cfg.serv_ws_port))
    };
    let (upstream, is_unix_sock) = get_tm_rpc_laddr(&app.cfg).c(d!())?;

    let builder = Web3ServerBuilder {
        upstream,
        upstream_is_unix_sock: is_unix_sock,
        http,
        ws,
        state: app.ledger.api_state.clone(),
    };

    if builder.upstream_is_unix_sock {
        omit!(fs::remove_file(&builder.upstream));
    }

    builder.build().start();

    Ok(())
}

// return: addr, is_unix_sock
fn get_tm_proxy_laddr(cfg: &DaemonCfg) -> Result<(String, bool)> {
    let cfgpath = format!("{}/config/config.toml", &cfg.tendermint_home_dir);
    let cfg = TendermintConfig::load_toml_file(&cfgpath).c(d!())?;
    let res = match &cfg.proxy_app {
        TmAddr::Tcp {
            peer_id: _,
            host,
            port,
        } => (format!("{}:{}", host, port), false),
        TmAddr::Unix { path } => (path.to_owned(), true),
    };
    Ok(res)
}

// return: addr, is_unix_sock
fn get_tm_rpc_laddr(cfg: &DaemonCfg) -> Result<(String, bool)> {
    let cfgpath = format!("{}/config/config.toml", &cfg.tendermint_home_dir);
    let cfg = TendermintConfig::load_toml_file(&cfgpath).c(d!())?;
    let res = match &cfg.rpc.laddr {
        TmAddr::Tcp {
            peer_id: _,
            host,
            port,
        } => (format!("{}:{}", host, port), false),
        TmAddr::Unix { path } => (path.to_owned(), true),
    };
    Ok(res)
}
